package com.erp.service.util.net;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;





import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.HTTP;

import android.util.Log;

import com.erp.service.util.IOHelper;
import com.erp.service.util.JSONHelper;


/**
 * 请求类
 * @author ccl
 */
public class NDHttpRequest {
	private static final String TAG = "NDHttpRequest";
	private static final String USER_AGENT = "Mozilla/4.5";
	protected Map<String, String> defaultHeader;
	protected String encoding;
	protected HttpClient client;
	private OnResponsedListener responseListener;

	/**
	 * 设置响应监听器
	 * @param listener 响应监听器
	 */
	public void setResponseListener(OnResponsedListener listener) {
		responseListener = listener;
	}

	/**
	 * 设置默认投信息
	 * @param headers
	 */
	public void setDefaultHeader(Map<String, String> headers) {
		defaultHeader.putAll(headers);
	}

	/**
	 * ND请求类构造函数,使用UTF-8编码,使用默认的DefaultHttpClient
	 * 
	 * @param defaultHeader
	 *            默认的请求头信息
	 */
	public NDHttpRequest(Map<String, String> defaultHeader) {
		this(defaultHeader, getClient());
	}

	/**
	 * 连接超时时间
	 */
	public static int CONNECTION_TIMEOUT = 30 * 1000;
	/**
	 * Socket超时
	 */
	public static int SOCKET_TIMEOUT = 10 * 60 * 1000;
	/**
	 * 所有域下的最大并发请求数
	 */
	public static final int MAX_TOTAL_CONNECTION = 40;
	/**
	 * 每个域下的最大并发请求数
	 */
	public static final int MAX_CONNECTIONS_PER_ROUTE = 40;
	public static AbstractHttpClient httpClient = null;
	/**
	 * 初始化请求客戶端
	 * @return 请求客户端
	 */
	public static  HttpClient getClient() {
		    
			if(httpClient!=null)
				return httpClient;
			// use following code to solve Adapter is detached error
			// refer:
			// http://stackoverflow.com/questions/5317882/android-handling-back-button-during-asynctask
			BasicHttpParams params = new BasicHttpParams();

			SchemeRegistry schemeRegistry = new SchemeRegistry();
			schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
			final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
			schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));

			// Set the timeout in milliseconds until a connection is
			// established.
			HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
			// Set the default socket timeout (SO_TIMEOUT)
			// in milliseconds which is the timeout for waiting for data.
			HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
			ConnManagerParams.setMaxTotalConnections(params, MAX_TOTAL_CONNECTION);
			ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(MAX_CONNECTIONS_PER_ROUTE));
			ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
			httpClient = new DefaultHttpClient(cm, params);
			
			
			//add gzip support
			httpClient.addRequestInterceptor(new GzipRequestInterceptor());
			httpClient.addResponseInterceptor(new GzipResponseInterceptor());

		return httpClient;
	}

	/**
	 * ND请求类构造函数,使用UTF-8编码
	 * 
	 * @param defaultHeader
	 *            默认的请求头信息
	 * @param client
	 *            请求客户端
	 */
	public NDHttpRequest(Map<String, String> defaultHeader, HttpClient client) {
		this(defaultHeader, client, HTTP.UTF_8);
	}

	/**
	 * ND请求类构造函数
	 * 
	 * @param defaultHeader
	 *            默认的请求头信息
	 * @param client
	 *            请求客户端
	 * @param encoding
	 *            请求编码可以传入HTTP.UTF_8
	 */
	public NDHttpRequest(Map<String, String> defaultHeader, HttpClient client, String encoding) {
		this.defaultHeader = defaultHeader;
		this.client = client;
		if (encoding == "" || encoding == null)
			encoding = HTTP.UTF_8;
		this.encoding = encoding;
	}

	/**
	 * 设置http头信息
	 * 
	 * @param message
	 *            HttpGet对象或 HttpPost对象
	 * @param headers
	 *            要添加的http头的键值对
	 * @return
	 */
	protected HttpUriRequest setHeaders(HttpUriRequest request, Map<String, String> headers) {
		for (Entry<String, String> entry : headers.entrySet()) {
			request.setHeader(entry.getKey(), entry.getValue());
		}
		return request;
	}

	/**
	 * 向指定URL发送GET方法的请求
	 * 
	 * @author ccl
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param params
	 *            请求参数，请求参数应该是name1=value1&name2=value2的形式 键值对内容中不能包含=和&符号
	 * @param RequestMethod
	 *            GET或POST
	 * @return URL所代表远程资源的响应
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 * @throws AuthenticationException
	 * @throws InvalidParameterException
	 */
	public ResponsedResult send(String url, String params, String requestMethod) throws ClientProtocolException, IOException, InvalidParameterException,
			AuthenticationException {
		HttpEntity entity = null;
		if (requestMethod == RequestMethod.POST)
			entity = new UrlEncodedFormEntity(null);
		return send(url, JSONHelper.toNameValuePairList(params), defaultHeader, requestMethod, entity);
	}

	/**
	 * 向指定URL发送GET方法的请求
	 * 
	 * @author ccl
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param params
	 *            请求参数，请求参数应该是name1=value1&name2=value2的形式 键值对内容中不能包含=和&符号
	 * @param RequestMethod
	 *            GET或POST
	 * @return URL所代表远程资源的响应
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 * @throws AuthenticationException
	 * @throws InvalidParameterException
	 */
	public ResponsedResult send(String url, String params, String requestMethod, HttpEntity entity) throws ClientProtocolException, IOException,
			InvalidParameterException, AuthenticationException {
		return send(url, JSONHelper.toNameValuePairList(params), defaultHeader, requestMethod, entity);
	}

	/**
	 * 向指定URL发送GET方法的请求
	 * 
	 * @author ccl
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param params
	 *            传入的参数键值对，内部会将传入的值进行UrlEncode
	 * @param RequestMethod
	 *            GET或POST
	 * @return URL所代表远程资源的响应
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 * @throws AuthenticationException
	 * @throws InvalidParameterException
	 */
	public ResponsedResult send(String url, List<NameValuePair> params, String requestMethod, HttpEntity entity) throws ClientProtocolException, IOException,
			InvalidParameterException, AuthenticationException {
		return send(url, params, defaultHeader, requestMethod, entity);
	}

	/**
	 * @author ccl 连接URL与参数返回最终地址
	 * @param url
	 *            URL
	 * @param params
	 *            参数键值对LIST
	 * @return
	 */
	private String joinUrlParams(String url, List<NameValuePair> params) {
		String paramString = URLEncodedUtils.format(params, encoding);
		if (url.contains("?"))
			url = url + "&" + paramString;
		else
			url = url + "?" + paramString;
		if (url.endsWith("?"))
			url = url.substring(0, url.length() - 1);
		return url;
	}

	/**
	 * 向指定URL发送GET方法的请求
	 * 
	 * @author ccl
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param params
	 *            传入的参数键值对，内部会将传入的值进行UrlEncode
	 * @param headers
	 *            http头信息
	 * @param RequestMethod
	 *            GET或POST
	 * @return URL所代表远程资源的响应
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 * @throws AuthenticationException
	 */
	public ResponsedResult send(String url, List<NameValuePair> params, Map<String, String> headers, String requestMethod, HttpEntity entity)
			throws ClientProtocolException, IOException, InvalidParameterException, AuthenticationException {
		url = joinUrlParams(url, params);
		Log.d(TAG, "GET:" + url);
		if (requestMethod.equalsIgnoreCase(RequestMethod.GET)) {
			return get(url, headers);
		} else if (requestMethod.equalsIgnoreCase(RequestMethod.POST)) {
			return post(url, entity, headers);
		}
		throw new InvalidParameterException("The requestMethod must be POST or GET");
	}

	/**
	 * 向指定URL发送GET方法的请求
	 * 
	 * @author ccl
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param params
	 *            请求参数，请求参数应该是name1=value1&name2=value2的形式，需要自己进行UrlEncode
	 * @param headers
	 *            http头信息
	 * 
	 * @return URL所代表远程资源的响应
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 * @throws AuthenticationException
	 */
	public ResponsedResult get(String url, Map<String, String> headers) throws ClientProtocolException, IOException, AuthenticationException {

		return send(new HttpGet(url), headers);
	}

	/**
	 * 向指定URL发送Post方法的请求
	 * 
	 * @author ccl
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param headers
	 *            http头信息
	 * 
	 * @return URL所代表远程资源的响应
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 * @throws AuthenticationException
	 */
	public ResponsedResult post(String url, HttpEntity entity, Map<String, String> headers) throws ClientProtocolException, IOException,
			AuthenticationException {
		HttpPost postMethod = new HttpPost(url);
		postMethod.setHeader("User-Agent", USER_AGENT);
		postMethod.setEntity(entity);
		Log.d(TAG, "POST:" + url + " data:" + entity.toString());
		return send(postMethod, headers);
	}

	/**
	 * 向指定URL发送Post方法的请求
	 * 
	 * @author ccl
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param headers
	 *            http头信息
	 * 
	 * @return URL所代表远程资源的响应
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 * @throws AuthenticationException
	 */
	public ResponsedResult upload(String url, List<NameValuePair> params, File file,long startPosition,UploadProgressListener listener) throws ClientProtocolException, IOException, AuthenticationException {
		url = joinUrlParams(url, params);
		if(startPosition!=0)
			file= IOHelper.trancStart(file.getPath() + ".tmp", new FileInputStream(file), file.length() - startPosition, startPosition);
		FileEntity entity = new NdFileEntity(file, "binary/octet-stream",startPosition,listener);
		return post(url, entity, defaultHeader);
	}

	/**
	 * @author ccl
	 * @param request
	 *            请求
	 * @param headers
	 *            需要添加的http头
	 * @return
	 * @throws IOException
	 *             IO读写异常
	 * @throws ClientProtocolException
	 *             客户端协议异常
	 */
	protected ResponsedResult send(HttpUriRequest request, Map<String, String> headers) throws ClientProtocolException, IOException, AuthenticationException {
		int statusCode = 0;
		String statusDescription = "";
		request.setHeader("User-Agent", USER_AGENT);
		request.setHeader("Cookie", "");
		Log.d(TAG, "headers:" + headers.toString());
		setHeaders(request, headers);
		HttpResponse httpResponse;
		try {
			httpResponse = client.execute(request);
			if (responseListener != null) {
				HttpResponse tempResponse = responseListener.onResponsed(client, request, httpResponse);
				if (tempResponse != null)
					httpResponse = tempResponse;
			}
			// statusCode == 200 正常
			statusCode = httpResponse.getStatusLine().getStatusCode();
			statusDescription = httpResponse.getStatusLine().getReasonPhrase();
			Log.d(TAG, "statuscode = " + statusCode + ",statusDescription=" + statusDescription);
			return new ResponsedResult(httpResponse, request);

		} catch (ClientProtocolException e) {
			Log.e(TAG, e.toString());
			throw e;
		} catch (IOException e) {
			Log.e(TAG, e.toString());
			throw e;
		} finally {
			// request.abort();
		}
	}

}
